// this is an attempt to create a general-purpose abstract claas for o game of Go.
// The first such attempt was "Shobu.java", but it is a failure:
// it doesn't contain a general method for nextMove(), etc...

package com.klangner.go.app.rules;

import java.util.Vector;

import com.klangner.game.board.IBoard;
import com.klangner.game.board.Move;

public abstract class GoGame {

  public GoGame() {
    listeners = new Vector();
  }

  public GoGame(int boardSize,
                Player blackPlayer, Player whitePlayer) {
    super();

    //this.boardSize = boardSize;
    turn = IBoard.BLACK_STONE;

    listeners = new Vector();
    priorityListeners = new Vector();

    goPosition = new BoardModel(boardSize);
//    goPosition.setLastMove(new Move(GoConstants.WHITE));

    changedLocations = new Vector<GobanLocation>();
    currentMove = new Move();


    this.blackPlayer = blackPlayer;
    this.blackPlayer.setGame(this);
    this.whitePlayer = whitePlayer;
    this.whitePlayer.setGame(this);

    // register the 2 players as game listeners
    gameResult = GoConstants.InProgress;
    nrCapturedStones = 0;
    consecutivePasses = 0;
  }

  // the main interface method: it performs the setup and starts the game
  public void playGame() {
    newGameSetup();

    while (!gameOver())
      playNextMove();
  }

  public void newGameSetup(){
    emptyGoban();
    turn = IBoard.BLACK_STONE;
    gameResult = GoConstants.InProgress;

    blackPlayer.initPlayer();
    whitePlayer.initPlayer();

    int oldBoardSize = goPosition.getSize();

    oldGoPosition = null;
    goPosition = new BoardModel( oldBoardSize );
  }

  // this method returns only after a new move was played
  public void playNextMove() {
    Player currentPlayer;

    if (turn == IBoard.BLACK_STONE)
      currentPlayer = blackPlayer;
    else
      currentPlayer = whitePlayer;

    if (currentPlayer == null || goPosition == null) {
        System.out.println( "ERROR in GoGame.playNextMove" );
        System.exit( 1 );
    }
    currentPlayer.makeMove(goPosition);
  }

  public abstract boolean gameOver();

  public int getGameResult() {
    if (!gameOver()) gameResult = GoConstants.InProgress;
    return gameResult;
  }

  protected void switchTurn() {
    if (turn == IBoard.BLACK_STONE) turn = IBoard.WHITE_STONE;
    else turn = IBoard.BLACK_STONE;
  }

  // this method is validating a position resulted my the last move
  protected int validatePosition(BoardModel position) {

    return 1; // valid position
  }

  private void emptyGoban() {
    nrCapturedStones = 0;
    consecutivePasses = 0;

    goPosition.emptyGoban();
  }

  /**
   * Make a new move.
   * x, y are the coordinates of the new move.
   * Returns > 0 if the move is valid and 0 otherwise.
   */
  public int newMove(int x, int y) {
    // todo: use the validate method (with a temp GoPosition)

    // check if the move was a Pass
    if ((x == GoConstants.PASS) || (y == GoConstants.PASS)) {
      // one more consecutive pass
      consecutivePasses++;
      switchTurn();

      return 1; // Pass move
    }
    else {
      nrCapturedStones = 0;
      consecutivePasses = 0;

      GobanLocation  stone;

      try {
        oldGoPosition = (BoardModel)goPosition.cloneCopy();
      } catch (CloneNotSupportedException e) {
          System.out.println(e);
        }

//      stone = goPosition.addNewStone(x, y, turn);
//      stone.computeLiberties();

//      Debug.log("setNewMove called from GoGame.newMove, on " + goPosition);
      nrCapturedStones = goPosition.makeMove(x, y, turn).size();

      switchTurn();

      notifyPositionChanged(new GoGameEvent(this, oldGoPosition, goPosition));

      return 1;
    }
  }

  public void addListener(GoGameListener goGameListener) {
    if (!listeners.contains(goGameListener))
      listeners.addElement(goGameListener);
  }

  public void removeListener(GoGameListener goGameListener) {
    listeners.removeElement(goGameListener);
  }

  public void addPriorityListener(GoGameListener goGameListener) {
    if (!listeners.contains(goGameListener))
      priorityListeners.addElement(goGameListener);
  }

  public void removePriorityListener(GoGameListener goGameListener) {
    priorityListeners.removeElement(goGameListener);
  }


  private synchronized void notifyPositionChanged(GoGameEvent goGameEvent) {
    // todo: make this thread-safe!!!

    // First notify the priority listeners

    java.util.Enumeration e = priorityListeners.elements();
    GoGameListener goGameListener;

    while (e.hasMoreElements()) {

      goGameListener = (GoGameListener)e.nextElement();
      goGameListener.positionChanged(goGameEvent);
    }

    // Then notify the regular listeners

    e = listeners.elements();
    while (e.hasMoreElements()) {
      goGameListener = (GoGameListener)e.nextElement();
      goGameListener.positionChanged(goGameEvent);

        /*
      PositionChangedThread thread = new PositionChangedThread(
        (GoGameListener)e.nextElement(), goGameEvent );

      thread.start();
      */
    }
  }

  /*
  class PositionChangedThread extends Thread {
    public PositionChangedThread( GoGameListener goGameListener,
                                  GoGameEvent goGameEvent  ) {
        super();

        fGameListener = goGameListener;
        fGoGameEvent = goGameEvent;
    }

    public void run() {
        fGameListener.positionChanged( fGoGameEvent );
        try {
            sleep(100);
        } catch (Exception e){}
    }

    private GoGameListener fGameListener;
    private GoGameEvent fGoGameEvent;
  }
  */

  private BoardModel oldGoPosition = null;
  public BoardModel goPosition = null;

  //public int boardSize;

  protected int turn = IBoard.BLACK_STONE;

  // the two players
  protected Player blackPlayer = null;
  protected Player whitePlayer = null;

  protected Vector<GobanLocation> changedLocations = null;
  protected Move currentMove;

  protected int gameResult = GoConstants.InProgress;
  protected int nrCapturedStones = 0;
  protected int consecutivePasses = 0;

  private Vector listeners = null;
  private Vector priorityListeners = null;
}
